//
// SecureLogin.cs
//
// Copyright (c) 2007 Lukas Lipka.
//

using System;
using System.Collections.Generic;
using System.Text;
using System.Security.Cryptography;

using IcqSharp.Packets;
using IcqSharp.Util;

namespace IcqSharp.Connections
{
    public class SecureLogin : LoginConnection
    {
        // This is a constant string we append when we
        // compute the md5 login hash
        private static string you_own_aol = "AOL Instant Messenger (SM)";

        public SecureLogin (Session session, string uin, string password)
            : base (session, uin, password)
        {
        }

        protected override void HandleData (byte[] data)
        {
            // Start the login procedure when we receive the hello
            // command from the server (always comes from channel 1)
            if (data[0] == FlapPacket.Id && data[1] == 0x01)
            {
                FlapPacket flap = new FlapPacket (0x01);
                flap.AddDword(0x00000001);
                flap.AddDword(0x80030004);
                flap.AddDword(0x10000000);
                flap.SetSequence(Session.GetSequenceNumber());
                Write(flap.Write());

                SnacPacket auth_req = new SnacPacket(0x17, 0x06);
                auth_req.AddTlv(new Tlv(0x0001, Uin));
                auth_req.AddTlv(new Tlv(0x004B));
                auth_req.AddTlv(new Tlv(0x005A));
                auth_req.SetSequence(Session.GetSequenceNumber());
                Write(auth_req.Write());

                return;
            }

            if (data[1] != 0x02 && data[1] != 0x04)
                Log.Error("Invalid channel for login packet");

            SnacPacket packet = null;

            try
            {
                packet = SnacPacket.Parse(data);
            }
            catch (Exception e)
            {
                Log.Error("Failed to parse login SnacPacket");
                Log.Error(e);
                return;
            }

            if (packet.Family == 0x17 && packet.Command == 0x07)
            {
                ushort length = EndianBitConverter.Big.ToUInt16(packet.Data, 0);
                string authkey = Encoding.UTF8.GetString(packet.Data, 2, length);

                byte[] magic = Encoding.UTF8.GetBytes (authkey + password + you_own_aol);

                MD5 md5 = MD5.Create();
                byte[] hash = md5.ComputeHash(magic);
                md5.Clear();

                SnacPacket p = new SnacPacket(0x17, 0x02);
                p.AddTlv(new Tlv(0x01, uin));
                p.AddTlv(new Tlv(0x03, "IcqSharp"));
                p.AddTlv(new Tlv(0x25, hash));
                p.SetSequence(Session.GetSequenceNumber());
                Write(p.Write());
            }

            if (packet.Family == 0x17 && packet.Command == 0x03)
            {
                TlvList tlvs = TlvList.Parse(packet.Data);

                if (tlvs [0x08] != null)
                {
                    ushort error_code = tlvs[0x08].ToUInt16 ();
                    Log.Error (Errors.GetError (error_code));

                    OnLoginFailed(null, EventArgs.Empty);

                    return;
                }

                string server = tlvs[0x05].ToString ();
                byte[] cookie = tlvs[0x06].Data;

                Log.Debug("MD5 login finished for '{0}'", uin);

                GotCookie(uin, server, cookie);

                // Finished - we have the cookie so disconnect
                Disconnect();
            }
        }
    }
}
